/*
 * Copyright 2013-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.cloud.nacos.client;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.cloud.nacos.NacosConfigProperties;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;

/**
 * @author xiaojing
 * @author pbting
 */
@Setter
@Getter
public class NacosPropertySource
        extends MapPropertySource {

    /**
     * Nacos Group.
     */
    private final String group;

    /**
     * Nacos dataID.
     */
    private final String dataId;

    /**
     * timestamp the property get.
     */
    private final Date timestamp;

    /**
     * Whether to support dynamic refresh for this Property Source.
     */
    private final boolean isRefreshable;

    NacosPropertySource(String group,
                        String dataId,
                        Map<String, Object> source,
                        Date timestamp,
                        boolean isRefreshable) {
        super(String.join(NacosConfigProperties.COMMAS, dataId, group), source);
        this.group = group;
        this.dataId = dataId;
        this.timestamp = timestamp;
        this.isRefreshable = isRefreshable;
    }

    public NacosPropertySource(List<PropertySource<?>> propertySources,
                               String group,
                               String dataId,
                               Date timestamp,
                               boolean isRefreshable) {
        this(group, dataId, getSourceMap(group, dataId, propertySources), timestamp, isRefreshable);
    }

    private static Map<String, Object> getSourceMap(String group,
                                                    String dataId,
                                                    List<PropertySource<?>> propertySources) {
        if (CollectionUtils.isEmpty(propertySources)) {
            return Collections.emptyMap();
        }
        // If only one, return the internal element, otherwise wrap it.
        if (propertySources.size() == 1) {
            PropertySource propertySource = propertySources.get(0);
            if (propertySource != null && propertySource.getSource() instanceof Map source) {
                return source;
            }
        }

        Map<String, Object> sourceMap = new LinkedHashMap<>();
        List<PropertySource<?>> otherTypePropertySources = new ArrayList<>();
        for (PropertySource<?> propertySource : propertySources) {
            if (propertySource == null) {
                continue;
            }
            if (propertySource instanceof MapPropertySource mapPropertySource) {
                // If the Nacos configuration file uses "---" to separate property name,
                // propertySources will be multiple documents, and every document is a
                // map.
                // see org.springframework.boot.env.YamlPropertySourceLoader#load
                Map<String, Object> source = mapPropertySource.getSource();
                sourceMap.putAll(source);
            } else {
                otherTypePropertySources.add(propertySource);
            }
        }

        // Other property sources which is not instanceof MapPropertySource will be put as
        // it is,
        // and the internal elements cannot be directly retrieved,
        // so the user needs to implement the retrieval logic by himself
        if (!otherTypePropertySources.isEmpty()) {
            sourceMap.put(String.join(NacosConfigProperties.COMMAS, dataId, group), otherTypePropertySources);
        }
        return sourceMap;
    }

    public boolean isRefreshable() {
        return isRefreshable;
    }

}
